﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Unity.Collections;
using UnityEngine;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;

namespace TeoGames.Mesh_Combiner.Scripts.Extension {
	public static class EnumerableExtension {
		public static float[] MinMax(this IEnumerable<float> arr) {
			var prev = 1f;
			var res = arr.OrderByDescending(h => h).ToArray();

			for (var i = 0; i < res.Length; i++) {
				var cur = Mathf.Clamp(res[i], 0, 1);

				if ((prev - cur) <= .000001f) {
					if (prev < 1) {
						if (i - 2 >= 0) {
							res[i - 1] = prev + Mathf.Min((res[i - 2] - res[i - 1]) / 2, 0.000001f);
						} else {
							res[i - 1] = Mathf.Min(prev + 0.000001f, 1);
						}
					}

					if (cur > 0) cur = Mathf.Max(cur - 0.000001f, 0);

					prev = res[i] = cur;
				} else {
					prev = cur;
				}
			}

			return res;
		}

		public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T, int> action) {
			var i = 0;
			foreach (var item in enumeration) action(item, i++);
		}

		public static async Task ForEach<T>(this IEnumerable<T> enumeration, Func<T, int, Task> action) {
			var i = 0;
			foreach (var item in enumeration) await action(item, i++);
		}

		public static async Task ForEach<T>(this IEnumerable<T> enumeration, Func<T, Task> action) {
			foreach (var item in enumeration) await action(item);
		}

		public static TR[] Convert<T, TR>(this List<T> enumeration, Func<T, TR> action) {
			var i = 0;
			var res = new TR[enumeration.Count];

			foreach (var item in enumeration) res[i++] = action(item);

			return res;
		}

		public static T[] FilterKeys<T>(this T[] input, int[] keys) {
			var cnt = keys.Length;
			var res = new T[cnt];
			for (var i = 0; i < cnt; i++) res[i] = input[keys[i]];

			return res;
		}

		// Generated by Chat GPT :)
		public static T[] GetRandomElements<T>(this T[] array, int n) {
			if (n > array.Length) {
				throw new ArgumentException("N must be less than or equal to the length of the array.");
			}

			var result = new T[n];

			var i = 0;
			while (i < n) {
				var index = Random.Range(0, array.Length);

				if (!result.Contains(array[index])) {
					result[i] = array[index];
					i++;
				}
			}

			return result;
		}

		public static void ForEach<T>(this IEnumerable<T> enumeration, Action<T> action) {
			foreach (var item in enumeration) action(item);
		}

		public static string ToPlainString<T>(this IEnumerable<T> enumeration) {
			return string.Join(", ", enumeration.Select(v => v.ToString()).ToArray());
		}

		public static IEnumerable<T> NotNull<T>(this IEnumerable<T> enumeration) where T : Object {
			return enumeration.Where(i => i);
		}

		public static T TakeFirst<T>(this IEnumerable<T> enumeration, Func<T, float> action) where T : Object =>
			enumeration.TakeFirst(null, action);

		public static T TakeFirst<T>(this IEnumerable<T> enumeration, T def, Func<T, float> action) where T : Object {
			return enumeration
				.DefaultIfEmpty(def)
				.OrderBy(action)
				.First();
		}

		public static T[] Take<T>(this T[] enumeration, int start, int count) {
			var res = new T[count];
			var end = start + count;

			for (var i = start; i < end; i++) res[i - start] = enumeration[i];

			return res;
		}

		public static NativeArray<T> ToNativeArray<T>(this IEnumerable<T> enumeration)
			where T : struct {
			return new NativeArray<T>(enumeration.ToArray(), Allocator.Temp);
		}
	}
}